/************************************************************************/
/*    Author: Scott Bevin                                               */
/************************************************************************/

#pragma once

#include <list>
#include <vector>
#include <algorithm>

#include "../Body.h"
#include "../Rectangle.h"

#include "Ogre.h"

#include <iostream>

namespace XF
{
	class QuadTreeNode
	{
	private:
		static bool smTreeStructureChangedThisFrame;

		unsigned int mTargetObjects;
		unsigned int mMaxDepth;

		std::list<Body*> mObjects;

		QuadTreeNode *mParentNode;
		std::vector<QuadTreeNode*> mChildNodes;

		RectangleF mRect;
		unsigned int mDepth;

		/**
		attempts to place the object into the proper node from this point downwards, 
		this function is not capable of propagating upwards through the tree.
		*/
		bool PlaceBodyIntoNode(Body* body);

		/**
		spawns 4 children node
		*/
		void SplitNode();

		/**
		deletes all the children of this node
		*/
		void DeleteChildNodes();

		void RenderNode(Ogre::ManualObject *manObj);

		void CollideObjectWithNode(std::list<Body*>::iterator it);

	public:
		QuadTreeNode(QuadTreeNode &copyNode);
		QuadTreeNode(QuadTreeNode *parent, int depth, float x, float y, float w, float h);
		~QuadTreeNode();

		/**
		this function will add an object into the tree, subdividing if necessary and possible
		*/
		bool AddBody(Body* body);

		/**
		remove object will remove an object and unnecessary nodes
		*/
		bool RemoveBody(Body* body);

		/**
		counts the number of objects held by the tree from this node downwards
		*/
		int GetNoOfObjects();

		int GetNoOfObjectsThisNodeHolds(){ return (int)mObjects.size(); }

		bool IsRootNode(){ return (mParentNode == NULL); }

		bool HasChildNodes() { return mChildNodes[0] != NULL; }
		std::vector<QuadTreeNode*> GetChildNodes(){return std::vector<QuadTreeNode*>(mChildNodes);}

		int TargetObjects(){ return mTargetObjects; }
		void TargetObjects(int targetObjects){ mTargetObjects = targetObjects; }

		int MaxDepth(){ return mMaxDepth; }
		void MaxDepth(int maxDepth){ mMaxDepth = maxDepth; }
		void RenderNodeInCout()
		{
			for(unsigned int i = 0; i < mDepth; ++i)
				std::cout << ".";

			std::cout << "Node contains " << GetNoOfObjectsThisNodeHolds() << " objects\n";

			if(HasChildNodes())
			{
				for(int i = 0; i < 4; ++i)
					mChildNodes[i]->RenderNodeInCout();
			}
		}

		void Render(Ogre::ManualObject *manObj);

		void CollideAllBodies();

		static bool TreeStructureChangedThisFrame(){return smTreeStructureChangedThisFrame;}
		static void ResetTreeStructureChangedBool(){ smTreeStructureChangedThisFrame = false; }
	};
}